package cn.gson.oasys.common.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@Aspect //申明是个切面
@Component //申明是个spring管理的bean
@Order(3) //order越小，则该该切面中的通知越先被执行。
public class LogAspectj {

    //声明sl4j 日志
    private static Logger logger = LoggerFactory.getLogger(LogAspectj.class);

    //设置在ManagerController的login方法的切点
    @Pointcut("execution(public * cn.gson.oasys.services.*..*(..))")
    public void serviceAspect(){ }
    /**
     * 之前
     */
    @Before(value = "serviceAspect()")
    public void beforeMethod(JoinPoint joinPoint){
        //切点，获取方法名
        String methodName = joinPoint.getSignature().getName();
        //切点，获取方法参数
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        //打印日志
        logger.info("{}—准备进去；方法名:{}；方法参数:{}",LocalDateTime.now(),methodName,args);
    }
    /**
     * 之后
     */
    @After(value = "serviceAspect()")
    public void afterMethod(JoinPoint joinPoint){
        //切点，获取方法名
        String methodName = joinPoint.getSignature().getName();
        logger.info("{}—:已经进去;方法名:{}。",LocalDateTime.now(),methodName);
    }
    /**
     * 返回
     */
    @AfterReturning(pointcut = "serviceAspect()" , returning = "result")
    public Object afterReturnMethod(JoinPoint joinPoint, Object result){
        //切点，获取方法名
        String methodName = joinPoint.getSignature().getName();
        logger.info("{}—:返回;方法名:{};返回结果:{};。",LocalDateTime.now(),methodName,"end.result<"+result+">");
        return result;
    }
    /**
     * 异常
     */
    @AfterThrowing(pointcut = "serviceAspect()" , throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Exception ex){
        //切点，获取方法名
        String methodName = joinPoint.getSignature().getName();
        logger.info("{}—:出现异常;方法名:{};异常信息:{};。",LocalDateTime.now(),methodName,"end.ex message<\"+ex+\">");
    }
    /**
     * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
     * 环绕通知包含前置、后置、返回、异常通知；ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
     * 且环绕通知必须有返回值，返回值即目标方法的返回值
     * @param point
     */
    @Around(value = "serviceAspect()")
    public Object aroundMethod(ProceedingJoinPoint point){
        Object result = null;
        String methodName = point.getSignature().getName();
        try {
            //前置通知
            logger.info("{}—环绕前置通知—方法名:{};方法参数:{}。",LocalDateTime.now(),methodName,"<"+ Arrays.asList(point.getArgs())+">");
            //执行目标方法
            result = point.proceed();
            //返回通知
            logger.info("{}—环绕返回通知—方法名:{};返回结果:{}。",LocalDateTime.now(),methodName,"<"+ result+">");
        } catch (Throwable e) {
            //异常通知
            logger.info("{}—环绕异常通知—方法名:{};异常信息:{}。",LocalDateTime.now(),methodName,"<"+ e+">");
            throw new RuntimeException(e);
        }
        //后置通知
        logger.info("{}—环绕后置通知—方法名:{}；end。",LocalDateTime.now(),methodName);
        return result;
    }
}
